from google.appengine.ext import db
from apnetmodels.apnetusers import APNETUser
from google.appengine.ext.db.polymodel import PolyModel
import datetime 
import time
from google.appengine.api import memcache
import logging

mem_client = memcache.Client()
MEMCACHE_TIME = 10800

class Country(db.Model):
    """---Country model----"""
    country_name = db.StringProperty(default="France")
    country_shortname = db.StringProperty("FR")

    def __repr__(self):
        return unicode(self.country_name)

class syndic(db.Model):
    #country = db.ReferenceProperty(Country)
    type =  db.StringProperty(verbose_name = "Type de societe", choices=set(["Professional", "Volunteer"]))
    name = db.StringProperty(verbose_name ="Raison sociale", required=True)
    creator = db.ReferenceProperty(APNETUser, required=True, collection_name='syndic_creator_set')
    adress = db.StringProperty(verbose_name = "Adresse", multiline=True)
    additional_adress = db.StringProperty(verbose_name = "Complement d'adresse")
    postalcode = db.StringProperty(verbose_name = "Code postal")
    ville = db.StringProperty(verbose_name = "Ville")
    phone = db.StringProperty(verbose_name = "Phone number (1)")
    phone_2 = db.StringProperty(verbose_name = "Phone number (2)")
    fax = db.StringProperty(verbose_name = "Fax", default='not provided')
    email = db.StringProperty(verbose_name = "E-mail", default='not provided')
    wwwsite = db.StringProperty(verbose_name = "Site internet", default='not provided')
    capital = db.FloatProperty(verbose_name = "Capital", default=0.000)
    siret = db.StringProperty(verbose_name = "SIRET")
    ape = db.StringProperty(verbose_name = "APE")
    carteprof_g = db.StringProperty(verbose_name = "Carte professionnelle G")
    carteprof_t = db.StringProperty(verbose_name = "Carte professionnelle T")
    caissegarant = db.StringProperty(verbose_name = "Caisse de garantie")
    n_dadherent = db.StringProperty(verbose_name = "# d'adherent")
    caisse_adress = db.StringProperty(verbose_name = "Adresse caisse de garantie")
    tva_num = db.StringProperty(verbose_name = "# de TVA intracommunautaire")
    code_fournisseur = db.StringProperty(verbose_name = "Code fournisseur")
    storing_years = db.FloatProperty(verbose_name = "Nb d'annee de conservation", default=3.0)
    note_title = db.StringProperty(verbose_name = "Entete edition")
    #sindic_logo = db.BlobProperty(verbose_name = "Logo", required=False, default=None)
    lastchange_date = db.DateTimeProperty(auto_now=True)
    lastchange_by = db.ReferenceProperty(APNETUser, required=True, collection_name='syndic_updater_set')

    def get_prdefine_template(self, type_of_list):
        
        ret = []
        if type_of_list == 'lot_types':
            ret = [("APPT", "APPARTEMENT", "Habitation"),
                   ("APPP", "APPARTEMENT PROFESSIONNEL", "Habitation"),
                   ("BUR", "BUREAU", "Habitation"),
                   ("CHBR", "CHAMBRE", "Habitation"),
                   ("GAR", "GARAGE", "Garage ou box"),
                   ("PKSS", "PARKING S/S", "Garage ou box"),
                   ("PARK", "PARKING", "Garage ou box"),
                   ("BOX", "BOX", "Garage ou box"),
                   ("CAVE", "CAVE", "Cave"),
                   ("BOUT", "BOUTIQUE", "Commerce"),
                   ("MAG", "MAGASIN", "Commerce"),
                   ("DIV", "DIVERS", "Autre"),
                   ("GREN", "GRENIER", "Autre"),
                   ("CELL", "CELLIER", "Autre"),
                   ("TERR", "TERRASSE", "Autre"),
                   ("JARD", "JARDIN", "Autre")] 
        elif type_of_list == 'work_types':
            ret = ["Administration",
                   "Agent immobilier",
                   "Architecte",
                   "Ascensoriste",
                   "Assureur",
                   "Avocat",
                   "Charpentier",
                   "Chauffagiste",
                   "Couvreur",
                   "Electricien",
                   "Electronicien/TV",
                   "Expert diagnostic amiante",
                   "Expert diagnostic DPE",
                   "Expert diagnostic gaz",
                   "Fournisseur eau",
                   "Fournisseur electricite",
                   "Fournisseur gaz",
                   "Fournisseur internet",
                   "Huisseries/portes",
                   "Informaticien",
                   "Jardinier / Espaces verts",
                   "Macon/carreleur",
                   "Mainteneur",
                   "Menuisier",
                   "Notaire",
                   "Panneaux/Pancartes",
                   "Peintre/Revetement/Ravalement",
                   "Plombier",
                   "Police",
                   "Securite/Alarme/Interphonie",
                   "Serurerier",
                   "Syndic",
                   "Verrier/Miroiterie",
                   "Vidangeur/Assainissement"
                   ]
           
        return ret

    def set_predefine_worktypes(self):
    
        lotworks_template = self.get_prdefine_template('work_types')
        lw_reg = []
        for w in self.type_of_work_set.run():
            lw_reg.append(w.label)
                    
        for w in lotworks_template:
            if not unicode(w) in lw_reg:
                new_type_of_work = type_of_work(label=unicode(w), syndic=self)
                new_type_of_work.put()
                while not new_type_of_work.is_saved():
                    time.sleep(2) 

        return lotworks_template
        
    
    def set_predefine_lottypes(self):
        
        lottypes_template = self.get_prdefine_template('lot_types')
        lt_reg = []
        for lt in self.lt_syndic_set.run():
            lt_reg.append(lt.shortname)
                    
        for lt in lottypes_template:
            if not unicode(lt[0]) in lt_reg:
                new_lot_type = lot_types(shortname=unicode(lt[0]), name=unicode(lt[1]),  groupe=unicode(lt[2]), syndic=self)
                new_lot_type.put()
                while not new_lot_type.is_saved():
                    time.sleep(2) 

        return lottypes_template

    def __repr__(self):
        return unicode(self.name)
    
class syndicForUser(db.Model):
    syndic = db.ReferenceProperty(reference_class = syndic, verbose_name = "Syndic", collection_name = 'syndic_users')
    user = db.ReferenceProperty(reference_class = APNETUser, verbose_name = "Person", collection_name = 'user_syndics')

class contact(PolyModel):
    default_phone = db.StringProperty(verbose_name = "Primary phone number", required=False)
    default_email = db.StringProperty(verbose_name = "Email", required=False)
    address = db.StringProperty(verbose_name = "Address",required=False, multiline=True)
    wwwsite = db.StringProperty(verbose_name = "Site internet", required=False)
    syndic = db.ReferenceProperty(reference_class = syndic, verbose_name = "Syndic", collection_name = 'syndic_contacts')
    lastchange_by = db.ReferenceProperty(APNETUser, required=False)
    lastchange_date = db.DateTimeProperty(auto_now=True)

    def __repr__(self):
        name = ''
        if self.class_name() == 'person':
            name = "%s %s" % (self.first_name, self.last_name)
        elif self.class_name() == 'company':
            name = self.name
        return unicode(name)

class additional_adress(db.Model):
    contact = db.ReferenceProperty(reference_class = contact, verbose_name = 'Contact', collection_name = 'add_adressess')    
    street = db.StringProperty(verbose_name = "Street", multiline=True)    
    pobox = db.StringProperty(verbose_name = "PO Box")    
    neighborhood = db.StringProperty(verbose_name = "Neighborhood")    
    city = db.StringProperty(verbose_name = "City")    
    state = db.StringProperty(verbose_name = "State/Province")    
    zip = db.StringProperty(verbose_name = "ZIP/Postal Code")    
    country = db.StringProperty(verbose_name = "Country/Region", default="France")    
    title = db.StringProperty(verbose_name = "Title",required=False)

    def __repr__(self):
        ret = ""
        if self.title:
            ret = "%s (%s)" % (self.address, self.title)
        else:
            ret =  self.address
        return unicode(ret)

class additional_phone(db.Model):
    contact = db.ReferenceProperty(reference_class = contact, verbose_name = 'Contact', collection_name = 'add_phones')    
    phone = db.PhoneNumberProperty(verbose_name = "Phone",required=True)
    title = db.StringProperty(verbose_name = "Title",required=False)

    def __repr__(self):
        ret = ""
        if self.title:
            ret = "%s (%s)" % (self.phone, self.title)
        else:
            ret =  self.phone
        return unicode(ret)


class additional_email(db.Model):
    contact = db.ReferenceProperty(reference_class = contact, verbose_name = 'Contact', collection_name = 'add_emails')    
    email = db.EmailProperty(verbose_name = "Email",required=True)
    title = db.StringProperty(verbose_name = "Title",required=False)

    def __repr__(self):
        ret = ""
        if self.title:
            ret = "%s (%s)" % (self.email, self.title)
        else:
            ret =  self.email
        return unicode(ret)
    
class person(contact):
    first_name = db.StringProperty(verbose_name = "First name", required=True)
    last_name = db.StringProperty(verbose_name = "Last name", required=True)
    cell_phone = db.StringProperty(verbose_name = "Cell phone", required=False)
    home_phone = db.StringProperty(verbose_name = "Home phone", required=False)
    office_phone = db.StringProperty(verbose_name = "Office phone", required=False)
    birthday = db.DateProperty(verbose_name = "Birthday", required=False)

class company(contact):
    name = db.StringProperty(verbose_name = "Company name", required=True)
    fax_number = db.StringProperty(verbose_name = "Fax", required=False)
    phone = db.StringProperty(verbose_name = "Phone", required=False)

class staff(db.Model):
    company = db.ReferenceProperty(reference_class = company, verbose_name = "Company", collection_name = 'company_staff', required=True)
    person = db.ReferenceProperty(reference_class = person, verbose_name = "Person", collection_name = 'position', required=True)
    position = db.StringProperty(verbose_name = "Position", required=False, default="")
    comment = db.TextProperty(verbose_name = "Comment", required=False)
    
    def __repr__(self):
        return "%s %s. %s. %s" % (self.person.last_name, self.person.first_name, self.company.name, self.position)

class immeuble(db.Model):
    syndic = db.ReferenceProperty(reference_class = syndic, verbose_name = "Syndic", collection_name = 'imm_syndic_set', required=True)
    co_syndic = db.BooleanProperty(verbose_name = 'Co-syndic') 
    adress = db.StringProperty(verbose_name = "Adresse", multiline=True)
    additional_adress = db.StringProperty(verbose_name = "Complement d'adresse", multiline=True)
    street = db.StringProperty(verbose_name = "Street", multiline=True)    
    pobox = db.StringProperty(verbose_name = "PO Box")    
    neighborhood = db.StringProperty(verbose_name = "Neighborhood")    
    city = db.StringProperty(verbose_name = "City")    
    state = db.StringProperty(verbose_name = "State/Province")    
    zip = db.StringProperty(verbose_name = "ZIP/Postal Code")    
    country = db.StringProperty(verbose_name = "Country/Region", default="France")    
    name = db.StringProperty(verbose_name ="Nom de l'immeuble", required=True)
    chargedusyndic_date = db.DateProperty(verbose_name ="Date de prise en charge du Syndic") 
    mandat_num = db.StringProperty(verbose_name = "# de mandat")
    yearbegin_date = db.DateProperty(verbose_name ="Date de debut d'exercice", required=False) 
    yearend_date = db.DateProperty(verbose_name ="Date de fin d'exercice", required=False)  
    sale_date = db.DateProperty(verbose_name ="Date de cession", required=False)  
    charges_period = db.StringProperty(required=False, choices=set(['Month', 'Quarter', 'Year']))
    chargesheating_period_id = db.StringProperty(required=False, choices=set(['Month', 'Quarter', 'Year']))
    billdetails = db.BooleanProperty(verbose_name = "Ventilation des comptes coproprietaires en sous compte") 
    unionchairman_name = db.StringProperty(verbose_name = "President du conseil")
    union_digicode  = db.StringProperty(verbose_name = "Digicode")
    union_phone = db.StringProperty(verbose_name = "Conseil telephone")
    default_fee = db.FloatProperty(verbose_name = "Montant des honoraires HT/Trimes", default=3.0, required=False )
    VAT_processing = db.BooleanProperty(verbose_name = "Traitement de la TVA") 
    code_INSEE = db.StringProperty(verbose_name = "Code INSEE")
    VAT_processing_charges = db.BooleanProperty(verbose_name = "Depense H.T.") 
    quote_recoverable = db.BooleanProperty(verbose_name = "Quote part recuperable") 
    quote_VAT = db.BooleanProperty(verbose_name = "Quote part TVA") 
    bulding_completion_date = db.DateProperty(verbose_name ="Date d'achevement des travaux", required=False)  
    building_cert_date = db.DateProperty(verbose_name ="Date de certificat de conformite", required=False) 
    building_permit_date = db.DateProperty(verbose_name ="Date de permis de construire", required=False) 
    building_permit_number = db.StringProperty(verbose_name = "# de permis")
    union_cons_last_date = db.DateProperty(verbose_name ="Date du Conseil Syndical") 
    GA_last_date = db.DateProperty(verbose_name ="Date de l'Assemblee Generale", required=False) 
    GA_address = db.StringProperty(verbose_name = "Lieu de l'Assemblee Generale", multiline=True)
    bottom_text = db.StringProperty(verbose_name = "Texte apparaissant en bas du bordereau")
    water_cnt = db.BooleanProperty(verbose_name = "Eau froide")
    hotwater_cnt = db.BooleanProperty(verbose_name = "Eau chaude")
    heating_cnt = db.BooleanProperty(verbose_name = "Chauffage")
    currency = db.StringProperty(verbose_name = "Device", choices=('EUR','USD'))
    form_comment = db.StringProperty(verbose_name = "Commentaire")
    note = db.StringProperty(verbose_name = "Note", multiline=True)
    frais_1 = db.FloatProperty(verbose_name = "Frais de relance 1", default=0.0)
    frais_2 = db.FloatProperty(verbose_name = "Frais de relance 2", default=0.0)
    frais_3 = db.FloatProperty(verbose_name = "Frais de relance 3", default=0.0)
    frais_4 = db.FloatProperty(verbose_name = "Frais de relance 4", default=0.0)
    honoraires_ht = db.FloatProperty(verbose_name = "Honoraires trimestriel HT", default=0.0)
    lastchange_date = db.DateTimeProperty(auto_now=True)
    lastchange_by = db.ReferenceProperty(APNETUser, required=False, collection_name='imm_updater_set',verbose_name = "Derniere modification par")
    tantiem_sum = db.FloatProperty(verbose_name = 'Tantiem summary')
    
    def get_lots(self):
        lots = []
        owners = {}
        for lo in self.lot_owner_set.filter('is_current_owner =', True).filter('is_linked =', True):
            owners[lo.lot.number] = lo.owner    
        
        for l in self.lot_set.order('number').run():
            lotowner = ''
            if l.number in owners.keys():
                lotowner = owners[l.number]
            ck_list = []
            for lw in l.lotsckeys.order('charges_key').run():
                ck_list.append({'charges_key': lw.charges_key, 'tantieme': lw.tantieme})  
            lots.append({'lot': l, 'lot_owner': lotowner, 'ck_list': ck_list})
        return lots

    def calculate_tantiem_sum(self):
        t_sum = 0
        for l in self.lot_set.run():
            for t in l.lotsckeys.order('charges_key').run():
                t_sum += t.tantieme
        return float(t_sum)

    def set_tantiem_sum(self): 
        self.tantiem_sum = self.calculate_tantiem_sum()
        self.put()

    def get_summary_charges_key(self):
        memcache_key =  "summary_charges_key_" + str(self.key())
        s = mem_client.get(memcache_key)
        if not s:
            s = {}
            for l in self.lot_set.run():
                for t in l.lotsckeys.order('charges_key').run():
                    if unicode(t.charges_key) in s.keys():
                        s[unicode(t.charges_key)]['sum'] +=  t.tantieme
                    else:
                        s[unicode(t.charges_key)] = {'sum': t.tantieme, 'total':t.charges_key.th_tantiem_sum, 'charge_key': t.charges_key}
            mem_client.set(key=memcache_key,value=s,time=MEMCACHE_TIME)
        return s
        
    def get_prdefine_template(self, type_of_list):
        
        ret = []
        if type_of_list == 'charges':
            ret = [("01", "Charges communes generales"), 
                   ("02", "Charges Communes batiment"), 
                   ("03", "Charges habitation"), 
                   ("04", "Charges commerces"), 
                   ("05", "Charges parking"), 
                   ("06", "Charges caves"), 
                   ("07", "Charges jardins ouverts"), 
                   ("09", "Charges locaux divers"), 
                   ("10", "Depense batiment"), 
                   ("26", "Depense Escalier"), 
                   ("31", "Depense ascenseur"), 
                   ("38", "Charges escalier de service"), 
                   ("39", "Charges tapis"), 
                   ("40", "Depense VO"), 
                   ("47", "Travaux desobstruction VO"), 
                   ("50", "Charges chauffage"), 
                   ("59", "Charges repartiteurs"), 
                   ("60", "Depense Boite lettre"), 
                   ("67", "Depense clef"), 
                   ("69", "Charges antenne T.V."), 
                   ("71", "Depense interphone"), 
                   ("77", "Depense speciales"), 
                   ("80", "Charges garage"), 
                   ("81", "Charges Box"), 
                   ("90", "Charges eaux"), 
                   ("91", "Charges compteurs")]
        
        elif type_of_list == 'subaccounts':
            ret =  [("13","13100", unicode("SUBVENTIONS ACCORDEES EN INSTANCE DE VERSEMENT")), 
                    ("40","40100", unicode("FACTURES PARVENUES")), 
                    ("40","40800", unicode("FACTURES NON PARVENUES")), 
                    ("40","40900", unicode("FOURNISSEURS DEBITEURS")), 
                    ("42","42100", unicode("RENUMERATIONS DUES")), 
                    ("43","43100", unicode("SECURITE SOCIALE")), 
                    ("43","43200", unicode("AUTRES ORGANISMES SOCIAUX")), 
                    ("44","44100", unicode("ETAT ET AUTRES ORGANISMES - SUBVENTIONS")), 
                    ("44","44200", unicode("ETAT - IMPOTS ET VERSEMENTS ASSIMILES")), 
                    ("44","44300", unicode("COLLECTIVITES TERRITORIALES - AIDES")), 
                    ("45","45000", unicode("COPROPRIETAIRES")), 
                    ("45","45010", unicode("COPROPRIETAIRE - BUDGET PREVISIONNEL")), 
                    ("45","45030", unicode("COPROPRIETAIRE - AVANCES")), 
                    ("45","45040", unicode("COPROPRIETAIRE - EMPRUNTS")), 
                    ("45","45900", unicode("COPROPRIETAIRE - CREANCES DOUTEUSES")), 
                    ("46","46100", unicode("DEBITEURS DIVERS")), 
                    ("46","46200", unicode("CREDITEURS DIVERS")), 
                    ("47","47100", unicode("COMPTE EN ATTENTE D'IMPUTATION DEBITEUR")), 
                    ("47","47200", unicode("COMPTE EN ATTENTE D'IMPUTATION CREDITEUR")), 
                    ("48","48600", unicode("CHARGES PAYEES D'AVANCE")), 
                    ("48","48700", unicode("PRODUITS ENCAISSES D'AVANCE")), 
                    ("49","49100", unicode("COPROPRIETAIRES")), 
                    ("49","49200", unicode("PERSONNES AUTRES QUE LE COPROPRIETAIRES")), 
                    ("50","50100", unicode("COMPTE A TERME")), 
                    ("50","50200", unicode("AUTRE COMPTE")), 
                    ("51","51200", unicode("BANQUE REGROUPEMENT")), 
                    ("53","53000", unicode("CAISSE REGROUPEMENT")), 
                    ("60","60100", unicode("EAU FROIDE")), 
                    ("60","60110", unicode("EAU CHAUDE")), 
                    ("60","60200", unicode("ELECTRICITE")), 
                    ("60","60300", unicode("CHAUFFAGE, ENERGIE ET COMBUSTIBLE")), 
                    ("60","60400", unicode("ACHATS PRODUITS D'ENTRETIEN ET PETITS EQUIPEMENTS")), 
                    ("60","60500", unicode("MATERIEL")), 
                    ("60","60600", unicode("FOURNITURES")), 
                    ("61","61100", unicode("NETTOYAGE DES LOCAUX")), 
                    ("61","61200", unicode("LOCATIONS IMMOBILIERES")), 
                    ("61","61300", unicode("LOCATIONS MOBILIERES")), 
                    ("61","61400", unicode("CONTRATS DE MAINTENANCE")), 
                    ("61","61500", unicode("ENTRETIEN ET PETITES REPARATIONS")), 
                    ("61","61600", unicode("PRIMES D'ASSURANCES")), 
                    ("62","62100", unicode("RENUMERATIONS DU SYNDIC SUR GESTION COPROPRIETE")), 
                    ("62","62110", unicode("RENUMERATION DU SYNDIC")), 
                    ("62","62120", unicode("DEBOURS")), 
                    ("62","62130", unicode("FRAIS POSTAUX")), 
                    ("62","62200", unicode("AUTRES HONORAIRES DU SYNDIC")), 
                    ("62","62210", unicode("HONORAIRES TRAVAUX")), 
                    ("62","62220", unicode("PRESTATIONS PARTICULIERES")), 
                    ("62","62230", unicode("AUTRES HONORAIRES")), 
                    ("62","62300", unicode("RENUMERATIONS DE TIERS INTERVENANTS")), 
                    ("62","62400", unicode("FRAIS DU CONSEIL SYNDICAL")),
                    ("62","62900", unicode("ARRONDIS")),
                    ("63","63200", unicode("TAXE DE BALAYAGE")), 
                    ("63","63300", unicode("TAXE FONCIERE")), 
                    ("63","63400", unicode("AUTRES IMPOTS ET TAXES")), 
                    ("64","64100", unicode("SALAIRES")), 
                    ("64","64200", unicode("CHARGES SOCIALES ET ORGANISMES SOCIAUX")), 
                    ("64","64300", unicode("TAXES SUR LES SALAIRES")), 
                    ("64","64400", unicode("AUTRES (MEDECINE DU TRAVAIL, MUTUELLES, ETC.)")), 
                    ("66","66100", unicode("REMBOURSEMENT D'ANNUITE D'EMPRUNT")), 
                    ("66","66200", unicode("AUTRES CHARGES FINANCIERES ET AGIOS")), 
                    ("67","67200", unicode("TRAVAUX URGENTS")), 
                    ("67","67300", unicode("ETUDES TECHNIQUES, DIAGNOSTIC, CONSULTATION")), 
                    ("67","67700", unicode("PERTES SUR CREANCES IRRECOUVRABLES")), 
                    ("67","67800", unicode("CHARGES EXCEPTIONNELLES")), 
                    ("701","70110", unicode("PROVISIONS SUR OPERATIONS COURANTES")), 
                    ("703","70400", unicode("REMBOURSEMENTS D'ANNUITES D'EMPRUNTS")), 
                    ("71","71100", unicode("SUBVENTIONS")), 
                    ("71","71200", unicode("EMPRUNTS")), 
                    ("71","71300", unicode("INDEMNITES D'ASSURANCES")), 
                    ("71","71400", unicode("PRODUITS DIVERS")), 
                    ("71","71600", unicode("PRODUITS FINANCIERS")), 
                    ("71","71800", unicode("PRODUITS EXCEPTIONELS"))] 
        elif type_of_list == 'expenditures': 
            ret =  [("groupe", "000", "Personnel"),
                    ("item", "010", "Salaires gardiens et concierge",75, 25, "64100", "Debit",0.0),
                    ("item", "020", "Salaires employe d'immeuble",100, 0, "64100", "Debit",0.0),
                    ("item", "030", "Charges sociales gardien/concierge",75, 25, "64200", "Debit",0.0),
                    ("item", "040", "Charges employes d'immeuble",100, 0, "64200", "Debit",0.0),
                    ("item", "050", "Personnel societes exterieures",100, 0, "61400", "Debit",19.6),
                    ("item", "060", "Frais de telephone gardien",100, 0, "61400", "Debit",19.6),
                    ("item", "080", "Loyer loge gardien",75, 25, "61200", "Debit",0.0),
                    ("groupe", "100", "Fournitures et materiels"),
                    ("item", "110", "Produits d'entretien",100, 0, "60400", "Debit",19.6),
                    ("item", "120", "Materiel",0, 100, "60500", "Debit",19.6),
                    ("item", "170", "Fuel combustible",100, 0, "60300", "Debit",19.6),
                    ("item", "180", "Charbon combustible",100, 0, "60300", "Debit",19.6),
                    ("item", "190", "Fournitures diverses",100, 0, "60600", "Debit",19.6),
                    ("groupe", "200", "Services generaux"),
                    ("item", "210", "Eau froide consommation",100, 0, "60100", "Debit",19.6),
                    ("item", "220", "Eau chaude consommation",100, 0, "60110", "Debit",19.6),
                    ("item", "230", "Electricite",100, 0, "60200", "Debit",19.6),
                    ("item", "240", "Gaz",100, 0, "60300", "Debit",19.6),
                    ("item", "250", "Telephone",100, 0, "61400", "Debit",19.6),
                    ("item", "280", "Location compteurs",100, 0, "61400", "Debit",19.6),
                    ("item", "290", "Contrat de maintenance fourniture",100, 0, "61400", "Debit",19.6),
                    ("groupe", "300", "Services divers"),
                    ("item", "370", "Desinfection",100, 0, "61400", "Debit",19.6),
                    ("item", "380", "Desinsectisation",100, 0, "61400", "Debit",19.6),
                    ("item", "390", "Deratisation",100, 0, "61400", "Debit",19.6),
                    ("groupe", "400", "Contrats et entretiens"),
                    ("item", "410", "Contrat de maintenance",100, 0, "61400", "Debit",19.6),
                    ("item", "420", "Contrat chaudiere",100, 0, "61400", "Debit",19.6),
                    ("item", "430", "Contrat ascenseurs complet",73, 27, "61400", "Debit",19.6),
                    ("item", "440", "Contrat ascenseurs ",100, 0, "61400", "Debit",19.6),
                    ("item", "450", "Contrat vide-ordures",100, 0, "61400", "Debit",19.6),
                    ("item", "480", "Contrat espaces verts",100, 0, "61400", "Debit",19.6),
                    ("item", "490", "Contrat assurance", 0, 0, "61600", "Debit",19.6),
                    ("groupe", "500", "Travaux exceptionnels"),
                    ("item", "510", "Travaux d'entretien ",100, 0, "61500", "Debit",7.0),
                    ("item", "520", "Petits travaux ",100, 0, "61500", "Debit",7.0),
                    ("item", "530", "Gros travaux ",0, 100, "61500", "Debit",7.0),
                    ("item", "550", "Travaux ascenseur",0, 100, "61500", "Debit",7.0),
                    ("item", "560", "Travaux batiment",0, 100, "61500", "Debit",7.0),
                    ("item", "570", "Honoraires architecte",0, 0, "62300", "Debit",7.0),
                    ("item", "580", "Honoraires geometre",0, 0, "62300", "Debit",7.0),
                    ("item", "590", "Honoraires expert",0, 0, "62300", "Debit",7.0),
                    ("groupe", "600", "Contentieux - expertises"),
                    ("item", "610", "Honoraires huissier",0, 0, "62300", "Debit",19.6),
                    ("item", "620", "Honoraires avocat",0, 0, "62300", "Debit",19.6),
                    ("item", "630", "Frais de contentieux",0, 0, "62300", "Debit",19.6),
                    ("item", "640", "Frais d'expertise",0, 0, "62300", "Debit",19.6),
                    ("groupe", "700", "- Espaces exterieurs"),
                    ("item", "710", "Espaces verts entretien ",100, 0, "61400", "Debit",7.0),
                    ("item", "720", "Fournitures ",100, 0, "60600", "Debit",19.6),
                    ("item", "730", "Materiel",100, 0, "60500", "Debit",19.6),
                    ("item", "740", "Plantations",100, 0, "60600", "Debit",19.6),
                    ("groupe", "800", "Depenses ou recettes diverses"),
                    ("item", "810", "Impots et taxes ",100, 0, "63400", "Debit",0.0),
                    ("item", "820", "Impots fonciers",0, 100, "63300", "Debit",0.0),
                    ("item", "860", "Loyer",100, 0, "71400", "Credit",0.0),
                    ("item", "870", "Produits financiers",0, 0, "71600", "Credit",0.0),
                    ("item", "880", "Frais financiers",0, 0, "66200", "Debit",0.0),
                    ("item", "890", "Remboursement sinistre",0, 0, "71300", "Credit",0.0),
                    ("groupe", "900", "Frais de gestion"),
                    ("item", "910", "Honoraires de syndic",0, 100, "62100", "Debit",19.6),
                    ("item", "920", "Honoraires assemblee generale",0, 100, "62110", "Debit",19.6),
                    ("item", "930", "Honoraires sur travaux",0, 100, "62210", "Debit",19.6),
                    ("item", "940", "Honoraires procedure",0, 100, "62230", "Debit",19.6),
                    ("item", "950", "Vacations diverses",0, 100, "62220", "Debit",19.6),
                    ("item", "960", "Frais postaux convocation A.G",0, 100, "62130", "Debit",0.0),
                    ("item", "970", "Frais administratif",0, 100, "62120", "Debit",19.6),
                    ("item", "980", "Frais de poste",0, 100, "62130", "Debit",0.0),
                    ("item", "990", "Location de salle",0, 100, "61200", "Debit",19.6)]
        return ret
    
    def set_predefine_expenditures(self, au):
        
        exp_template = self.get_prdefine_template("expenditures")
        new_exp_type = None
        vat_dict = {}
        for i in VAT.all():
            vat_dict[i.value] = i
        subaccount_dict = {}    
        for s in self.subaccount_set.run():
            subaccount_dict[s.code] = s
        items = []
        for i in exp_template:
            if i[0] == "groupe": 
                new_exp_type = expenditure_type(
                                                key_name = i[1] + "_" + str(self.key().id()),
                                                immeuble = self,
                                                code = i[1],
                                                name = i[2],
                                                lastchange_by = au
                                                )
                new_exp_type.put()
                while not new_exp_type.is_saved():
                    time.sleep(0.5)
            if i[0] == "item" and new_exp_type:
                new_exp =  expenditure( key_name = i[1] + '_' + str(self.key().id()),
                                        type = new_exp_type,
                                        code=i[1], 
                                        title=i[2],
                                        TVA = vat_dict[i[7]],
                                        reduc = float(i[3]),
                                        deduc = float(i[4]),
                                        regroupe = False,
                                        direction = i[6],
                                        subaccount = subaccount_dict[i[5]],
                                        lastchange_by=au )
                #new_exp.put(is_cache = False)
                #while not new_exp.is_saved():
                #    time.sleep(0.5)
                items.append(new_exp) 

        db.put(items)
        return exp_template
    
    def set_predefine_subaccounts(self, au):
        
        subaccounts_template = self.get_prdefine_template("subaccounts")
        accounts = account.all()
        for a in accounts:
            for s in subaccounts_template:
                if a.code == s[0]:
                    new_acc = subaccount(key_name = s[1] + "_" + str(self.key().id()),
                                         account = a, 
                                         immeuble = self, 
                                         code = s[1], 
                                         name = s[2], 
                                         lastchange_by = au
                                         )
                    new_acc.put()
                    while not new_acc.is_saved():
                        time.sleep(2) 
        
        return subaccounts_template
        
    def set_predefine_chargeskeys(self, au):
            
        chargeskey_template = self.get_prdefine_template("charges")
        
        ck_reg = []
        for ck in self.chargeskeys.run():
            ck_reg.append(ck.code)
                
        for ck in chargeskey_template:
            if not unicode(ck[0]) in ck_reg:
                new_charge_key = charges_key(key_name = unicode(ck[0]) + "_" + str(self.key().id()),
                          immeuble = self, 
                          code = unicode(ck[0]), 
                          name = unicode(ck[1]),
                          lastchange_by = au
                          )
                new_charge_key.put()
                while not new_charge_key.is_saved():
                    time.sleep(2) 

        return chargeskey_template
    
    def __repr__(self):
        return unicode("%s (%s)" % (self.name, self.adress))
    
    def fill_exp_list(self, exp_obj):
        cache_key = str(self.key()) + '_expenditures_list'
        exp_list = mem_client.get(cache_key)
        if exp_list is None:
            self.get_exp_list()
        else:
            exp_list[str(exp_obj.key())] = exp_obj
            if not mem_client.replace(cache_key, exp_list):
                mem_client.add(cache_key, exp_list, time=MEMCACHE_TIME)
    
    def get_exp_list(self):
        cache_key = str(self.key()) + '_expenditures_list'
        exp_dict = mem_client.get(cache_key)
        if not exp_dict:
            exp_dict = {}
            for et in self.expenditure_type_set.order('code').run():
                for exp in et.expenditures.order('code').run():
                    exp_dict[str(exp.key())] = exp
            
            if not mem_client.replace(cache_key, exp_dict):
                mem_client.add(cache_key, exp_dict, time=MEMCACHE_TIME)
        
        exp_list = exp_dict.values()
        exp_list.sort(key= lambda x: x.code)
        return exp_list

    def fill_subacc_list(self, subacc_obj):
        cache_key = str(self.key()) + '_subaccounts_list'
        subacc_list = mem_client.get(cache_key)
        if subacc_list is None:
            self.get_subacc_list()
        else:
            subacc_list[str(subacc_obj.key())] = subacc_obj
            if not mem_client.replace(cache_key, subacc_list):
                mem_client.add(cache_key, subacc_list, time=MEMCACHE_TIME)
    
    def get_subacc_list(self):
        cache_key = str(self.key()) + '_subaccounts_list'
        subacc_dict = mem_client.get(cache_key)
        if not subacc_dict:
            subacc_dict = {}
            for sa in subaccount.all().filter('immeuble =', self).run():
                subacc_dict[str(sa.key())] = sa
            
            if not mem_client.replace(cache_key, subacc_dict):
                mem_client.add(cache_key, subacc_dict, time=MEMCACHE_TIME)
        
        subacc_list = subacc_dict.values()
        subacc_list.sort(key= lambda x: x.code)
        return subacc_list
    
    def fill_charges_key_list(self, ck_obj):
        cache_key = str(self.key()) + '_charges_key_list'
        ck_list = mem_client.get(cache_key)
        if ck_list is None:
            self.get_charges_key_list()
        else:
            ck_list[str(ck_obj.key())] = ck_obj
            if not mem_client.replace(cache_key, ck_list):
                mem_client.add(cache_key, ck_list, time=MEMCACHE_TIME)
    
    def get_charges_key_list(self):
        cache_key = str(self.key()) + '_charges_key_list'
        ck_dict = mem_client.get(cache_key)
        if not ck_dict:
            ck_dict = {}
            for ck in self.chargeskeys.order('code').run():
                ck_dict[str(ck.key())] = ck
            
            if not mem_client.replace(cache_key, ck_dict):
                mem_client.add(cache_key, ck_dict, time=MEMCACHE_TIME)
        
        ck_list = ck_dict.values()
        ck_list.sort(key= lambda x: x.code)
        return ck_list

class period_concept(db.Model):
    date_start = db.DateProperty(verbose_name = "Start date", required=True)
    date_finish = db.DateProperty(verbose_name = "Finish date", required=True)
    closed = db.BooleanProperty(verbose_name = "Budget closed", required=True, default=False)
    close_accounting = db.BooleanProperty(verbose_name = "Accounting closed", required=True, default=False)
    immeuble = db.ReferenceProperty(reference_class = immeuble, verbose_name = "Immeuble", required=True)
    lastchange_by = db.ReferenceProperty(APNETUser, required=True)
    lastchange_date = db.DateTimeProperty(auto_now=True)
    
    def __repr__(self):
        return unicode("%s - %s" % (self.date_start.strftime('%d/%m/%Y'), self.date_finish.strftime('%d/%m/%Y')))

    def get_previous(self):
        all_pc_q = self.immeuble.period_concept_set.order('date_start').run()
        if not all_pc_q:
            return None
        pc_previous = None
        pc_list_keys = []
        pc_list = []
        for p in all_pc_q:
            pc_list_keys.append(p.key())
            pc_list.append(p)
        p = pc_list_keys.index(self.key())
        if p > 0:
            pc_previous = pc_list[p-1]
        
        return pc_previous
    
    def get_next(self):
        all_pc_q = self.immeuble.period_concept_set.order('date_start').run()
        if not all_pc_q:
            return None
        pc_next = None
        pc_list_keys = []
        pc_list = []
        for p in all_pc_q:
            pc_list_keys.append(p.key())
            pc_list.append(p)
        p = pc_list_keys.index(self.key())
        if p < len(pc_list_keys) - 1:
            pc_next = pc_list[p+1]

        return pc_next 
        
    def get_budget_sum(self, budget_type=''):
        s = 0.0
        b_query = self.budget_item_set
        m_key = str(self.key()) + "_summary"
        if budget_type:
            m_key = str(self.key()) + "_summary_" + budget_type
            b_query = b_query.filter('type =', budget_type)
        
        m_sum = mem_client.get(m_key)
        if m_sum:
            s = m_sum
        else: 
            for i in b_query.run():
                s += i.amount
            mem_client.add(m_key, s, time=MEMCACHE_TIME)    
        return s 
    
    def get_fundraising_sum(self):
        s = 0
        for i in self.fundraising_set.run():
            s += i.amount
        return s 

    def get_ck_list(self, bitype='Regular'):
        
        imm_ck_sum = self.immeuble.get_summary_charges_key()
        p_ck_sum = self.get_sum_by_charges_keys(bitype)
        m_keys = list(set(imm_ck_sum.keys() +  p_ck_sum.keys()))
        m_ck = {} 
        empty_ck = {'sum': 0.0, 'total': None}
        empty_budget = {'amount': 0.0}
        for k in m_keys:
            if k in imm_ck_sum.keys():
                if m_ck and k in m_ck.keys():
                    m_ck[k] = dict(m_ck[k], **imm_ck_sum[k])
                else:
                    m_ck[k] = dict(imm_ck_sum[k], **empty_budget)
            if k in p_ck_sum.keys():
                if m_ck and k in m_ck.keys():
                    m_ck[k] = dict(m_ck[k], **p_ck_sum[k])
                else:
                    m_ck[k] = dict(p_ck_sum[k], **empty_ck)
        return m_ck

    def get_fr_distribution_4lot(self,lot, k):
        """ Amount distribution by chrges keys per lot """        
        pass
        
    def get_fundraising_distribution_v2(self, total_amount=None, b_type='Regular'):

        ck_merged = self.get_ck_list(b_type)
        budget = self.get_budget(b_type)
        k = 1.0
        total = 0.0
        budget_sum = self.get_budget_sum(b_type)
        ret = {}
        if total_amount:
            if budget_sum:
                k = float(total_amount)/float(budget_sum)
            else:
                return ret, 0, 0
            
        for lo in self.immeuble.lot_set.order('number').run():
            ret[unicode(lo)] = {'lot_owner': lo.get_current_owner(), 'lot': lo,'charges_keys':{}}
            lot_sum = 0.0
            budget_lot_sum = 0.0
            for ck in lo.lotsckeys:
                key_name = unicode(ck.charges_key)
                ret[unicode(lo)]['charges_keys'][key_name] = {'charges_key': ck.charges_key, 
                                                              'sum': ck_merged[unicode(ck.charges_key)]['sum'], 
                                                              'expenditures': {}, 
                                                              'tantieme': ck.tantieme,
                                                              'ck_amount': 0.0,     
                                                              'budget_ck_amount': 0.0
                                                              }
                norm_tantiem = 0
                if ck_merged[unicode(ck.charges_key)]['sum']:
                    norm_tantiem = float(ck.tantieme)/float(ck_merged[unicode(ck.charges_key)]['sum'])
                    if key_name in budget.keys():
                        ck_amount = 0.0
                        budget_ck_amount = 0.0
                        for bi in budget[key_name]['item_list']:
                            amount = norm_tantiem * bi['amount'] * k
                            b_amount = bi['amount'] * k
                            total += amount
                            lot_sum += amount
                            budget_lot_sum += b_amount
                            ck_amount += amount
                            budget_ck_amount += b_amount
                            ret[unicode(lo)]['charges_keys'][key_name]['expenditures'][unicode(bi['expenditure'])]={'expenditure': bi['expenditure'], 'amount': amount, 'bubget_amount': b_amount}
                        ret[unicode(lo)]['charges_keys'][key_name]['ck_amount'] = ck_amount
                        ret[unicode(lo)]['charges_keys'][key_name]['budget_ck_amount'] = budget_ck_amount 
            
            ret[unicode(lo)]['lot_sum'] = lot_sum 
            ret[unicode(lo)]['budget_lot_sum'] = budget_lot_sum
            
        return ret, total, budget_sum, ck_merged

    def get_fundraising_owners_sums(self, total_amount=None, b_type='Regular'):
        
        lots, sum_res, b_sum, ck_merged = self.get_fundraising_distribution_lo(total_amount, b_type)

        data = []
        for r in lots:
            owner =  getattr(lots[r][0]['lot_owner'], 'owner', None)
            if owner: 
                amount = sum(map(lambda l: l['lot_sum'], lots[r]))
                data.append({'owner': owner, 'amount': amount})
        
        control_sum = sum(map(lambda l: l['amount'], data))
        return data, control_sum, sum_res  

    def get_fundraising_distribution_lo(self, total_amount=None, b_type='Regular'):
        
        lots, sum_res, b_sum, ck_merged = self.get_fundraising_distribution_v2(total_amount, b_type)
        lo_codes = {}
        for l in  lots.keys():
            if  lots[l]['lot_owner']:
                keyname = "%s_%s" %  ( lots[l]['lot_owner'].owner, lots[l]['lot_owner'].code)
            else:
                keyname = 'NoOwner'
            if  keyname in lo_codes.keys():
                lo_codes[keyname].append({
                                          'lot': lots[l]['lot'], 
                                          'lot_owner':lots[l]['lot_owner'], 
                                          'charges_keys':lots[l]['charges_keys'],  
                                          'budget_lot_sum': lots[l]['budget_lot_sum'],
                                          'lot_sum': lots[l]['lot_sum'] })  
            else:
                lo_codes[keyname] = [{'lot': lots[l]['lot'], 
                                      'lot_owner':lots[l]['lot_owner'], 
                                      'charges_keys':lots[l]['charges_keys'], 
                                      'budget_lot_sum': lots[l]['budget_lot_sum'],  
                                      'lot_sum': lots[l]['lot_sum'] }]

        return lo_codes, sum_res, b_sum, ck_merged
         
    def get_fundraising_distribution_lo_print(self, total_amount=None, b_type='Regular'):
        
        lo_codes, calc_sum, b_sum, ck_merged = self.get_fundraising_distribution_lo(total_amount, b_type)
        lo_codes_list = []
        for l in  lo_codes:
            lots_list = []
            for i in lo_codes[l]:
                ck_o = i['charges_keys'].keys()
                ck_o.sort()
                ck_list = []
                for ck in ck_o:
                    exp_ordered = i['charges_keys'][ck]['expenditures'].keys()
                    exp_ordered.sort()
                    exp_list = []
                    for e in exp_ordered:
                        exp_list.append({
                                     'budget_amount':i['charges_keys'][ck]['expenditures'][e]['bubget_amount'],
                                     'amount': i['charges_keys'][ck]['expenditures'][e]['amount'], 
                                     'expenditure': i['charges_keys'][ck]['expenditures'][e]['expenditure']})
                    ck_list.append({
                                    'expenditures': exp_list,
                                    'charges_key': i['charges_keys'][ck]['charges_key'],
                                    'ck_amount': i['charges_keys'][ck]['ck_amount'],
                                    'budget_ck_amount': i['charges_keys'][ck]['budget_ck_amount'],
                                    'millieme': i['charges_keys'][ck]['tantieme'],
                                    'tantieme': i['charges_keys'][ck]['sum']
                                     })
                lo = None
                if i['lot_owner']:
                    lo = i['lot_owner'].owner
                lots_list.append({
                                      "lot": i['lot'],
                                      "budget_lot_sum": i['budget_lot_sum'],  
                                      "lot_sum": i['lot_sum'],
                                      "charges_keys": ck_list,})
                
            s_lots_list = sorted(lots_list, key=lambda l: l["lot"].number) 
                
            lo_codes_list.append({'owner': lo, 'lots': s_lots_list}) 
             
        return lo_codes_list, calc_sum, b_sum, ck_merged
  
    def get_sum_by_charges_keys(self, bitype='Regular'):
        
        memcache_key = 'period_sum_budget_%s_%s' % (bitype, str(self.key()))
        ret = mem_client.get(memcache_key)
        if not ret:
            ret = {}    
            for bv in self.budget_item_set.filter('type =', bitype).order('charges_key').run():
                if not str(bv.charges_key) in ret.keys():
                    ret[unicode(bv.charges_key)] = {'amount': bv.amount, 'charge_key': bv.charges_key}      
                else:
                    ret[unicode(bv.charges_key)]['amount'] += bv.amount
            mem_client.set(key = memcache_key, value = ret, time = MEMCACHE_TIME)
        return ret
    
    def get_budget(self, b_type=''):
        ret = {}
        budget_query = self.budget_item_set.order('charges_key')
        if b_type:
            budget_query = budget_query.filter('type =', b_type)
        for bv in budget_query.run():
            if not unicode(bv.charges_key) in ret.keys():
                ret[unicode(bv.charges_key)] = {'charge_key': bv.charges_key, 'item_list':[{'expenditure':bv.expenditure ,'amount': bv.amount}]}      
            else:
                ret[unicode(bv.charges_key)]['item_list'].append({'expenditure':bv.expenditure, 'amount': bv.amount }) 
        return ret

    def get_budget_v2(self, b_type=''):
        ret = {}
        budget_query = self.budget_item_set.order('charges_key')
        if b_type:
            budget_query = budget_query.filter('type =', b_type)
        for bv in budget_query.run():
            if not unicode(bv.charges_key) in ret.keys():
                ret[unicode(bv.charges_key)] = {'charge_key': bv.charges_key, 'expenditures':{unicode(bv.expenditure) :{'expenditure':bv.expenditure ,'amount': bv.amount}}}      
            else:
                ret[unicode(bv.charges_key)]['expenditures'][unicode(bv.expenditure)] = ({'expenditure':bv.expenditure, 'amount': bv.amount }) 
        return ret

    def get_budget_template_v2(self, b_type=''):

        prev_period = self.get_previous()
        next_period = self.get_next()
        
        budget =  self.get_budget_v2(b_type)
        prev_budget = {}
        if prev_period:
            prev_budget = prev_period.get_budget_v2(b_type)
        next_budget = {}
        if next_period:
            next_budget = next_period.get_budget_v2(b_type)
        
        ck_keys =  list(set(budget.keys() +  prev_budget.keys() + next_budget.keys()))
        ck_keys.sort()
        m_budget = []
        for i in ck_keys:
            i_dict = {}
            c_exp = []
            if i in  budget.keys():
                c_exp = budget[i]['expenditures'].keys()
                ck = budget[i]['charge_key']
            p_exp = []
            if i in  prev_budget.keys():
                p_exp = prev_budget[i]['expenditures'].keys()
                ck = prev_budget[i]['charge_key']
            n_exp = []
            if i in  next_budget.keys():
                n_exp = next_budget[i]['expenditures'].keys()
                ck = next_budget[i]['charge_key']
            exp_keys = list(set(c_exp + p_exp + n_exp))
            exp_keys.sort()
            i_dict = {'c_sum': 0, 'p_sum': 0, 'n_sum': 0}
            e_list = []
            for e in exp_keys:
                e_dict = {'c_amount': None, 'p_amount': None, 'n_amount': None} 
                if i in budget.keys():
                    if e in budget[i]['expenditures']:
                        e_dict['expenditure'] = budget[i]['expenditures'][e]['expenditure']
                        e_dict['c_amount'] = budget[i]['expenditures'][e]['amount']
                        i_dict['c_sum'] += budget[i]['expenditures'][e]['amount']
                if i in prev_budget.keys():
                    if e in prev_budget[i]['expenditures']:   
                        e_dict['expenditure'] = prev_budget[i]['expenditures'][e]['expenditure']
                        e_dict['p_amount'] = prev_budget[i]['expenditures'][e]['amount']
                        i_dict['p_sum'] += prev_budget[i]['expenditures'][e]['amount']
                if i in next_budget.keys():
                    if e in next_budget[i]['expenditures']:   
                        e_dict['expenditure'] = next_budget[i]['expenditures'][e]['expenditure']
                        e_dict['n_amount'] = next_budget[i]['expenditures'][e]['amount']
                        i_dict['n_sum'] += next_budget[i]['expenditures'][e]['amount']
                e_list.append(e_dict)
            i_dict['expenditures'] = e_list
            i_dict['charges_key'] = ck
            m_budget.append(i_dict)
        
        return m_budget, prev_period, next_period   
    
    
    
    def get_budget_template(self, b_type=''):
        ret = []
        budget = self.get_budget(b_type)
        ck_order =  budget.keys()
        ck_order.sort()
        for ck in ck_order:
            i_list = []
            exp_dict = {}
            for i in budget[ck]['item_list']:
                k_name = '%s %s' % (unicode(i['expenditure'].code), unicode(i['expenditure'].title)) 
                exp_dict[k_name] = i['amount']
            exp_order = exp_dict.keys()
            exp_order.sort()
            for i in exp_order:
                i_list.append({'name': i, 'amount': exp_dict[i]})
            ret.append({'charges_key': ck, 'expenditures': i_list})
        return ret 
    
    def get_budget_list(self, b_type=''):
        ret = {}
        budget_query = self.budget_item_set.order('charges_key')
        if b_type:
            budget_query = budget_query.filter('type =', b_type)
        for bv in budget_query.run():
            if not unicode(bv.charges_key) in ret.keys():
                ret[unicode(bv.charges_key)] = {'charge_key': bv.charges_key, 'item_list':[{'expenditure':bv.expenditure ,'amount': bv.amount}]}      
            else:
                ret[unicode(bv.charges_key)]['item_list'].append({'expenditure':bv.expenditure, 'amount': bv.amount }) 
        ret_list = []
        or_keys = ret.keys()
        or_keys.sort()
        for k in or_keys:
            ret_list.append(ret[k])
        return ret_list

class lot_types(db.Model):
    syndic = db.ReferenceProperty(reference_class = syndic, verbose_name = "Syndic", collection_name = 'lt_syndic_set')
    name = db.StringProperty(required=True, verbose_name = 'Libelle')
    shortname = db.StringProperty(required=True, verbose_name = 'Libelle abrege')
    groupe = db.StringProperty(required=False, verbose_name = 'Type De Lot', choices=set(["Habitation", "Garage ou box","Cave", "Commerce", "Autre"]))
    
    def __repr__(self):
        return unicode("%s [%s]" % (self.name, self.groupe))
    
    def to_dict(self):
        return dict([(p, unicode(getattr(self, p))) for p in self.properties()])
    
class lot(db.Model):
    immeuble = db.ReferenceProperty(reference_class = immeuble, verbose_name = "Immeuble", required=True)
    number = db.IntegerProperty(verbose_name = "Numero", required=True)
    lot_type = db.ReferenceProperty(reference_class = lot_types, verbose_name = "Nature", required=True)
    #syndic = db.ReferenceProperty(reference_class = syndic, verbose_name = "Syndic")
    batiment = db.StringProperty(verbose_name = "Batiment")
    escalier = db.StringProperty(verbose_name = "Escalier")
    etage = db.StringProperty(verbose_name = "Etage")
    surface = db.FloatProperty(verbose_name = "Surface", default=0.0)
    plan = db.StringProperty(verbose_name = "Plan")
    occupant = db.StringProperty(verbose_name = "Occupant")
    codeedf = db.StringProperty(verbose_name = "Code EDF")
    codegdf = db.StringProperty(verbose_name = "Code GDF")
    codetelecom = db.StringProperty(verbose_name = "Code Telecom")
    have_owner = db.BooleanProperty(verbose_name = 'Lot have current owner', default = False)
    lastchange_by = db.ReferenceProperty(APNETUser, required=True, collection_name='lot_updater_set')
    lastchange_date = db.DateTimeProperty(auto_now=True)
    
    def __repr__(self):
        return unicode("#%s (Etage: %s, Nature: %s, Surface: %s)" % (self.number, self.etage ,self.lot_type.shortname, self.surface))

    def get_current_owner(self):
        lo = None
        lo = self.owner_lots.filter('is_current_owner =', True).get()
        return lo
        
class lot_owner(db.Expando):
    owner = db.ReferenceProperty(reference_class = contact, verbose_name = "Contact", collection_name = 'lot_owners', required=True)
    code = db.StringProperty(verbose_name = "Code")
    share = db.FloatProperty(verbose_name = "Partage", default=1.0)
    lot = db.ReferenceProperty(reference_class = lot, verbose_name = "Lot", collection_name = 'owner_lots', required=False)
    purchase_date = db.DateProperty(verbose_name = "Date of purchase")
    sale_date = db.DateProperty(verbose_name = "Date of sale")
    immeuble = db.ReferenceProperty(reference_class = immeuble, verbose_name = "Tenement", required=True)
    is_linked = db.BooleanProperty(default = False, verbose_name = 'Owner linked with at least one lot')     
    is_current_owner = db.BooleanProperty(default = False, verbose_name = 'Current owner')
    lastchange_by = db.ReferenceProperty(APNETUser, required=True, collection_name='lotowner_updater_set')
    lastchange_date = db.DateTimeProperty(auto_now=True)
    
    def put(self, **kwargs):
        super(lot_owner, self).put(**kwargs)
        acc = account.get_by_key_name("45")
        on_list = unicode(self.owner).upper().split()
        owner_name = ""
        for i in range(len(on_list)-1):
            owner_name += on_list[i][0]
        owner_name += on_list[-1]
        if len(owner_name) >= 6:
            owner_name = owner_name[:6]
        else:
            owner_name += '_'*(6-(len(owner_name)))
        sacc_code = "45000" + owner_name + str(self.owner.key().id() % 7)
        sacc_key_name =  "45000_" + str(self.immeuble.key().id()) + "_" + str(self.owner.key().id())
        sacc = subaccount.get_by_key_name(sacc_key_name, parent=self.owner)   
        if not sacc:
            sacc = subaccount(key_name = sacc_key_name,
                              parent = self.owner, 
                              account = acc,
                              code = sacc_code,
                              name = "Appel de fonds Coproprietaire: %s" % str(self.owner),
                              immeuble = self.immeuble,
                              lastchange_by = self.lastchange_by)
            sacc.put()
        #get_or_insert(key_name = sacc_key_name, 
        #                                account = acc,
        #                                code = sacc_code,
        #                                name = "Appel de fonds Coproprietaire: %s" % str(self.owner),
        #                                immeuble = self.immeuble,
        #                                lastchange_by = self.lastchange_by)
        
    def __repr__(self):
        return unicode("%s [%s] (%s)" % (self.owner, self.code, self.lot))

    def get_budget_distribution(self, period):
        ck_d = {}
        ck_sum = self.immeuble.get_summary_charges_key()
        for ck in self.lot.lotsckeys.run():
            share = 0.0
            if ck_sum[unicode(ck)]:
                share = float(ck.tantieme)/float(ck_sum[unicode(ck)]['sum']) 
            key_sum = 0
            for  bv in period.budget_item_set.filter('charges_key =', ck.charges_key).run(projection=['amount']):
                key_sum += bv.amount
            ck_d[str(ck.charges_key)] = {'amount': float(key_sum * share), 'charges_key': ck.charges_key, 'tantieme': ck.tantieme}
        return ck_d
    
    def get_summary_charges_keys(self, period):
        ck_d = {}
        tantiem_price = period.calculate_tantiem_price()
        for ck in self.lot.lotsckeys.run():
            ck_d[unicode(ck.charges_key)] = {'amount': float(ck.tantieme * tantiem_price), 'charges_key': ck.charges_key, 'tantieme': ck.tantieme}
        return ck_d
    
    def get_summary_charges_keys_print(self, period):
        ck_d = []
        tantiem_price = period.calculate_tantiem_price()
        for ck in self.lot.lotsckeys.order('charges_key').run():
            ck_d.append({'amount': float(ck.tantieme * tantiem_price), 'charges_key': ck.charges_key, 'tantieme': ck.tantieme, 'sort_key':ck.charges_key.code })
        return ck_d
    
    def get_amount_distribution(self, period, total_amount):
        ck_d = {}
        k = 0.0
        budget_amount = period.get_budget_sum()
        tantiem_price = period.calculate_tantiem_price()
        if budget_amount > 0:
            k = float(total_amount)/float(budget_amount)
        for ck in self.lot.lotsckeys.run():
            ck_d[unicode(ck.charges_key)] = {'amount': float(ck.tantieme * tantiem_price * k), 'total_amount': float(ck.tantieme * tantiem_price),'charges_key': ck.charges_key, 'tantieme': ck.tantieme}
        return ck_d
    
    def get_amount_distribution_v2(self, period, total_amount):
        ck_d = {}
        k = 0.0
        budget_amount = period.get_budget_sum()
        tantiem_price = period.calculate_tantiem_price()
        if budget_amount > 0:
            k = float(total_amount)/float(budget_amount)
        for ck in self.lot.lotsckeys.run():
            ck_d[unicode(ck.charges_key)] = {'amount': float(ck.tantieme * tantiem_price * k), 'total_amount': float(ck.tantieme * tantiem_price),'charges_key': ck.charges_key, 'tantieme': ck.tantieme}
        return ck_d
        
class charges_key(db.Model):
    code = db.StringProperty(verbose_name = "Charges key code", required=True)
    name = db.StringProperty(verbose_name = "Charges key name", required=True)
    th_tantiem_sum = db.IntegerProperty(verbose_name = "Total Theoretical Tantieme", required=False)
    immeuble = db.ReferenceProperty(reference_class = immeuble, verbose_name = "Immeuble", required=True, collection_name='chargeskeys')    
    lastchange_by = db.ReferenceProperty(APNETUser, required=True)
    lastchange_date = db.DateTimeProperty(auto_now=True)

    def __repr__(self):
        return unicode("%s %s" % (self.code, self.name))
        
    def put(self, **kwargs):
        super(charges_key, self).put(**kwargs)
        self.immeuble.fill_charges_key_list(self)
        sck_memcache_key =  "summary_charges_key_" + str(self.immeuble.key())
        while not mem_client.delete(sck_memcache_key):
            time.sleep(1)
        keys_list = []
        for p_key in self.immeuble.period_concept_set.run(keys_only=True):
            keys_list.append(('period_sum_budget_Regular_%s' % str(p_key)))
            keys_list.append(('period_sum_budget_Additional_%s' % str(p_key)))
        if keys_list:
            while not mem_client.delete_multi(keys_list):
                time.sleep(1)
        return self.key()
        
class council(db.Model):
    lot_owner = db.ReferenceProperty(reference_class = contact, verbose_name = "Nom",  required=True)
    immeuble = db.ReferenceProperty(reference_class = immeuble, verbose_name = "Immeuble", required=True)    
    function = db.StringProperty(verbose_name = "Fonction", required=True)

    def __repr__(self):
        return unicode("%s (# %s)" % (self.lot_owner, self.function))    
    
class lot_budget_weights(db.Model):
    lot = db.ReferenceProperty(reference_class = lot, verbose_name = "Lot", required=True, collection_name='lotsckeys')
    charges_key = db.ReferenceProperty(reference_class = charges_key, verbose_name = "Charges key", required=True, collection_name='weightskeys')
    tantieme = db.IntegerProperty(verbose_name = "Tantieme", required=True, default=0)
    lastchange_by = db.ReferenceProperty(APNETUser, required=True)
    lastchange_date = db.DateTimeProperty(auto_now=True)

    def __repr__(self):
        return unicode("%s, %s, %s)" % (self.lot, self.charges_key, self.tantieme))    

    def put(self, **kwargs):
        super(lot_budget_weights, self).put(**kwargs)
        sck_memcache_key =  "summary_charges_key_" + str(self.lot.immeuble.key())
        while not mem_client.delete(sck_memcache_key):
            time.sleep(1)
        return self.key()
        
class account(db.Model):
    code = db.StringProperty(verbose_name = "Account number", required=True)
    name = db.StringProperty(verbose_name = "Account name", required=True)

    def __repr__(self):
        return unicode("%s %s" % (self.code, self.name))    

class VAT(db.Model):
    name = db.StringProperty(verbose_name = "VAT type", required=True)
    value = db.FloatProperty(verbose_name = "Value", required=True, default = 0.0)

    def __repr__(self):
        return unicode("%s: %s%%" % (self.name, self.value))     

class subaccount(db.Model):
    account = db.ReferenceProperty(reference_class = account, verbose_name = "Account", required=True, collection_name='subaccounts')
    code = db.StringProperty(verbose_name = "Subaccount number", required=True)
    name = db.StringProperty(verbose_name = "Subaccount name", required=True)
    balance = db.FloatProperty(verbose_name = "Subaccount balance", default=0.0)
    immeuble = db.ReferenceProperty(reference_class = immeuble, verbose_name = "Immeuble", required=True)
    lastchange_by = db.ReferenceProperty(APNETUser, required=True, collection_name='subacc_updater_set')
    lastchange_date = db.DateTimeProperty(auto_now=True)

    def __repr__(self):
        return unicode("%s %s " % (self.code, self.name))
        
    def put(self, **kwargs):
        super(subaccount, self).put(**kwargs)
        self.immeuble.fill_subacc_list(self)

class expenditure_type(db.Model):
    immeuble = db.ReferenceProperty(reference_class = immeuble, verbose_name = "Immeuble", required=True)
    code = db.StringProperty(verbose_name = "Code", required=True)
    name = db.StringProperty(verbose_name = "Title", required=True)
    lastchange_by = db.ReferenceProperty(APNETUser, required=True, collection_name='exptype_updater_set')
    lastchange_date = db.DateTimeProperty(auto_now=True)

    def __repr__(self):
        return unicode("%s [# %s]" % (self.name, self.code))    
        
class expenditure(db.Model):
    type = db.ReferenceProperty(reference_class = expenditure_type, verbose_name = "Groupe des depenses", collection_name='expenditures', required=True)
    code = db.StringProperty(verbose_name = "Code", required=True)
    title = db.StringProperty(verbose_name = "Libelle", required=True)
    TVA = db.ReferenceProperty(reference_class = VAT, verbose_name = "TVA", collection_name='vats', required=True) 
    deduc = db.FloatProperty(verbose_name = "Deductible", default=0)
    reduc = db.FloatProperty(verbose_name = "Recuperable", default=100)
    regroupe = db.BooleanProperty(verbose_name = "Regroupe")
    direction = db.StringProperty(verbose_name = "Credit/Debit", choices=set(["Debit", "Credit"]), default="Debit")
    subaccount = db.ReferenceProperty(reference_class = subaccount, verbose_name = "Compte", collection_name='subacc_expenditures', required=True)
    lastchange_by = db.ReferenceProperty(APNETUser, required=True, collection_name='exp_updater_set')
    lastchange_date = db.DateTimeProperty(auto_now=True)

    def __repr__(self):
        return unicode("%s %s (account: %s)" % (self.code, self.title, self.subaccount))
    
    def put(self, **kwargs):
        is_cache = kwargs.pop('is_cache', True)
        super(expenditure, self).put(**kwargs)
        if is_cache:
            self.type.immeuble.fill_exp_list(self)
    
    
class bankaccount(db.Model):
    name = db.StringProperty(verbose_name = "Nom de la Banque", required=True)
    bank_code =  db.StringProperty(verbose_name = "Code banque", required=True)
    cashdesk_code = db.StringProperty(verbose_name = "Code guichet", required=True)
    account_num = db.StringProperty(verbose_name = "# de compte", required=True)
    RIB_key = db.StringProperty(verbose_name = "cle RIB", required=True)
    domiciliation = db.StringProperty(verbose_name = "domiciliation", multiline=True)
    comment = db.StringProperty(verbose_name = "titulaire", multiline=True)
    IBAN = db.StringProperty(verbose_name = "IBAN", required=True)
    BIC = db.StringProperty(verbose_name ="BIC", required=True)
    immeuble = db.ReferenceProperty(reference_class = immeuble, verbose_name = "Immeuble", required=True)
    lastchange_by = db.ReferenceProperty(APNETUser, required=True)
    lastchange_date = db.DateTimeProperty(auto_now=True)
    
    def put(self, **kwargs):
        super(bankaccount, self).put(**kwargs)
        acc = account.get_by_key_name("51")
        sacc_code = "51200_" + str(self.account_num)[-5:]
        sacc_code = sacc_code.replace(" ", "_")
        sacc_code_t = sacc_code + "_T" 
        sacc_key_name =  "51200_" + str(self.immeuble.key().id()) + "_" + str(self.key().id())
        sacct_key_name = "51200_t_" + str(self.immeuble.key().id()) + "_" + str(self.key().id())
        sacc = subaccount.get_by_key_name(sacc_key_name, parent=self)   
        if not sacc:
            sacc = subaccount(key_name = sacc_key_name,
                              parent = self, 
                              account = acc,
                              code = sacc_code,
                              name = "Banque: %s" % unicode(self),
                              immeuble = self.immeuble,
                              lastchange_by = self.lastchange_by)
            sacc.put()
        sacc_t = subaccount.get_by_key_name(sacct_key_name, parent=self)
        if not sacc_t:
            sacc_t = subaccount(key_name = sacct_key_name,
                              parent = self, 
                              account = acc,
                              code = sacc_code_t,
                              name = "Cheque en attemte: %s" % unicode(self),
                              immeuble = self.immeuble,
                              lastchange_by = self.lastchange_by)
            sacc_t.put()
        
        return self.key()
    
    def __repr__(self):
        return unicode("%s (%s)" % (self.name, self.account_num))    

class bubget_tag(db.Model):
    tag = db.StringProperty(verbose_name = "Budget tag")
    assigned_date = db.DateTimeProperty(auto_now=True)
    assigned_by = db.ReferenceProperty(APNETUser, required=True)

class budget_item(db.Model):    
    immeuble = db.ReferenceProperty(reference_class = immeuble, verbose_name = "Immeuble", required=True)
    period = db.ReferenceProperty(reference_class = period_concept, verbose_name = "Account Period", required=True)
    expenditure = db.ReferenceProperty(reference_class = expenditure, verbose_name = 'Nature of expenditure', required=True)
    charges_key = db.ReferenceProperty(reference_class = charges_key, verbose_name = "Charges key", required=True)
    amount = db.FloatProperty(verbose_name = "Amount", required=True, default=0.0)
    state = db.StringProperty(verbose_name="State", required=True, default="Not approved", choices=set(["Approved", "Not approved"])) 
    bubget_tag = db.ReferenceProperty(reference_class = bubget_tag, verbose_name = "Budget tag", required=False)
    lastchange_by = db.ReferenceProperty(APNETUser, required=True)
    lastchange_date = db.DateTimeProperty(auto_now=True)
    type = db.StringProperty(verbose_name="Type", required=True, default="Regular", choices=set(["Regular", "Additional"]))
    def __repr__(self):
        return unicode("%s: %s, %s, %s" % (self.period, self.expenditure, self.charges_key, self.amount))    

    def put(self, **kwargs):
        try:
            k = self.key()
        except db.NotSavedError:
            k = None
        if k:
            old = budget_item.get(k)
            delta =  self.amount - old.amount
        else:
            delta =  self.amount
        super(budget_item, self).put(**kwargs)
        sum = self.period.get_budget_sum()
        sum += delta
        sum_t = self.period.get_budget_sum(budget_type=self.type)
        sum_t += delta
        m_key = str(self.period.key()) + "_summary"
        r = mem_client.set(key=m_key,value=sum,time=MEMCACHE_TIME)
        m_key = str(self.period.key()) + "_summary_" + self.type
        r = mem_client.set(key=m_key,value=sum_t,time=MEMCACHE_TIME)
        m_key = 'period_sum_budget_%s_%s' % (self.type, str(self.period.key()))
        while not mem_client.delete(m_key):
            time.sleep(1)
        return self.key() 

    def delete(self, **kwargs):
        sum = self.period.get_budget_sum() - self.amount
        sum_t = self.period.get_budget_sum(budget_type=self.type) - self.amount
        type = self.type
        super(budget_item, self).delete(**kwargs)
        m_key = str(self.period.key()) + "_summary"
        r = mem_client.set(key=m_key, value=sum, time=MEMCACHE_TIME)
        m_key = str(self.period.key()) + "_summary_" + type
        r = mem_client.set(key=m_key, value=sum_t, time=MEMCACHE_TIME)
        return True
        
    def save(self):
        bi = budget_item.all().filter('immeuble =', self.immeuble).filter('period =', self.period).filter('expenditure =', self.expenditure).filter('charges_key', self.charges_key).get()
        if bi and bi.key() != self.key():
            return 0
        else:
            delta = self.amount
            if bi:
                delta = self.amount - bi.amount
            self.period.budget += delta
            self.period.tantiem_price = self.period.budget/self.period.immeuble.tantiem_sum
            self.put()
            self.period.put()
            
class type_of_work(db.Model):
    syndic = db.ReferenceProperty(reference_class = syndic, verbose_name = "Syndic", required=True)
    label = db.StringProperty(verbose_name = "Type of work", required=True)
    def __repr__(self):
        return unicode("%s" % (self.label))    
    
class supplier(db.Model):
    syndic = db.ReferenceProperty(reference_class = syndic, verbose_name = "Syndic", required=True)
    contact = db.ReferenceProperty(reference_class = contact, verbose_name = "Supplier", required=True)
    type_of_work = db.ReferenceProperty(reference_class = type_of_work, verbose_name = "Type of work", required=True)
    contact_person = db.StringProperty(verbose_name = "Contact", required=False)
    
    def __repr__(self):
        return unicode("%s (%s)" % (self.contact, self.type_of_work))    
    
class invoice(db.Model):
    period = db.ReferenceProperty(reference_class = period_concept, verbose_name = "Account period", required=True)
    invoice_date = db.DateProperty(verbose_name = "Date facture", default = datetime.date.today(),required=True) 
    supplier = db.ReferenceProperty(reference_class = supplier, verbose_name = "Fournisseur", required=False)
    deadline = db.DateProperty(verbose_name = "Date facture", required=False) 
    number = db.StringProperty(verbose_name = "# de piece", required=True)
    expendature = db.ReferenceProperty(reference_class = expenditure, verbose_name = "Compte de depense", required=True)
    charges_key = db.ReferenceProperty(reference_class = charges_key, verbose_name = "Cle", required=True)
    title = db.StringProperty(verbose_name = "Libelle", required=False)
    amount = db.FloatProperty(verbose_name = "Montant TTC", required=True)
    tva = db.FloatProperty(verbose_name = "dont TVA", default=0.0)
    rental_charges = db.FloatProperty(verbose_name = "Montant charges locatives", required=False, default=0.0) 
    tax_deduction = db.FloatProperty(verbose_name = "Montant deduction fiscale", required=False, default=0.0)
    paid = db.BooleanProperty(default=False)
    paid_amount =  db.FloatProperty(verbose_name = "Montant paye", required=False, default=0.0)
    locked = db.BooleanProperty(default=False)
    pending_amount = db.FloatProperty(verbose_name = "Pending amount", required=False, default=0.0)
    lastchange_by = db.ReferenceProperty(APNETUser, required=True)
    lastchange_date = db.DateTimeProperty(auto_now=True)
    
    def __repr__(self):
        return unicode("#%s: %s [%s]" % (self.number, self.title, self.amount))    

class payment(db.Model):
    invoice = db.ReferenceProperty(reference_class = invoice, verbose_name = "Invoice", required=True)
    immeuble =  db.ReferenceProperty(reference_class = immeuble, verbose_name = "Immeuble", required=True)
    date = db.DateProperty(verbose_name = "Date paiemeent", default = datetime.date.today(),required=True) 
    amount = db.FloatProperty(verbose_name = "Montant a payer", required=True)
    responsible = db.ReferenceProperty(APNETUser, required=True, collection_name="responsible") 
    method_of_payment = db.StringProperty(verbose_name = "Mode de reglement", required=True, choices=set(["Remise manuelle", "Reglement manuel", "Reglement automatique"]))
    check_number = db.StringProperty(verbose_name = "# de cheque", required=False)
    approved_flag = db.BooleanProperty(verbose_name = 'Ok paiement definitif', default=False)
    approved_by = db.ReferenceProperty(APNETUser, required=False, collection_name="approved_by")
    lastchange_by = db.ReferenceProperty(APNETUser, required=True)
    lastchange_date = db.DateTimeProperty(auto_now=True)

    def __repr__(self):
        return unicode("%s. %s, Facture: %s " % (self.date, self.ammount, self.invoice))

class fundraising(db.Model):
    period = db.ReferenceProperty(reference_class = period_concept, verbose_name = "Account period", required=True)
    deadline = db.DateProperty(verbose_name = "Due Date", required=True)
    type = db.StringProperty(verbose_name="Type", required=True, default="Regular", choices=set(["Regular", "Additional"]))
    desc = db.StringProperty(verbose_name = "Description", required=True)
    amount = db.FloatProperty(verbose_name = "Amount", required=True, default=0.0)
    bankaccount = db.ReferenceProperty(reference_class = bankaccount, verbose_name = "Bank account", required=True)
    remarks = db.StringProperty(verbose_name = "Remarks", multiline=True)
    immeuble = db.ReferenceProperty(reference_class = immeuble, verbose_name = "Immeuble", required=True)
    commited = db.BooleanProperty(verbose_name = "Commited", default=False) 
    lastchange_by = db.ReferenceProperty(APNETUser, required=True)
    lastchange_date = db.DateTimeProperty(auto_now=True)
    
    def __repr__(self):
        return unicode("%s. %s" % (self.desc, self.deadline.strftime('%d/%m/%Y')))
    
    def update_general_ledger(self, au):
        if self.commited:
            return False, "This request already commited."
        
        imm = self.period.immeuble 
        imm_id = str(imm.key().id())
        fr_id = str(self.key().id())
        
        tacc_key_name = "70110_" + imm_id
        sacc_key_name  = "45000_" + imm_id
        racc_key_name  = "62900_" + imm_id
        transient_account = subaccount.get_by_key_name(tacc_key_name)
        if not transient_account:
            return False, "Transient account '70110' should be defined"
        sum_account = subaccount.get_by_key_name(sacc_key_name)
        if not sum_account:
            return False, "Consolidated account '45000' not defined" 
        
        owners, calc_amount, req_amount = self.period.get_fundraising_owners_sums(self.amount)
        if abs(calc_amount - self.amount) > 0.01:
            return False, "Rounding of calculation more than 0.01. Please validate data."
        gl_records = []
        payments = []
        accounts = {}
        for i in owners:
            sacc_key_name =  "45000_" + str(imm.key().id()) + "_" + str(i['owner'].key().id())
            owner_acc = subaccount.get_by_key_name(sacc_key_name, parent = i['owner']) 
            if not owner_acc:
                return False, ("Account for '%s' not defined" ) % (str(i['owner'])) 
            record = None
            d = round(i['amount'],2) 
            record = general_ledger(source = 'Generer un appel de fonds',
                                    number = 'ADF_' + fr_id,
                                    doc_date = self.lastchange_date.date(),
                                    ini_subaccount = owner_acc,
                                    description = self.desc,
                                    debit = d,
                                    contr_subaccount = transient_account,
                                    period = self.period,
                                    entry_type = self.type,
                                    reversal = False,
                                    lastchange_by = au,
                                    parent = self)
            gl_records.append(record)
            if owner_acc.code in accounts.keys():
                accounts[owner_acc.code].balance -= d
            else:
                owner_acc.balance -= d
                accounts[owner_acc.code] = owner_acc
            payment = coowner_payment(coowner = i['owner'],
                                      immeuble = self.immeuble,
                                      type = "Cheque",
                                      fundraising = self,
                                      amount =  d,
                                      parent = i['owner'],
                                      lastchange_by = au,
                                      cheque_date = None,
                                      bank_date = None) 
            payments.append(payment)

            
        record = None
        record = general_ledger(source = 'Generer un appel de fonds',
                                number = 'ADF_' + fr_id,
                                doc_date = self.lastchange_date.date(),
                                ini_subaccount = transient_account,
                                description = self.desc,
                                credit = self.amount,
                                contr_subaccount = sum_account,
                                period = self.period,
                                entry_type = self.type,
                                reversal = False,
                                lastchange_by = au,
                                parent = self)
        gl_records.append(record)
        transient_account.balance =  transient_account.balance + self.amount
        accounts[transient_account.code] = transient_account
        r = round((self.amount - calc_amount),2)
        if r:
            rounding_account = subaccount.get_by_key_name(racc_key_name)
            if not rounding_account:
                return False, "Rounding account '62900' not defined" 
            record = general_ledger(source = 'Generer un appel de fonds',
                                    number = 'ADF_' + fr_id,
                                    doc_date = self.lastchange_date.date(),
                                    ini_subaccount = rounding_account,
                                    description = self.desc,
                                    debit = r,
                                    contr_subaccount = sum_account,
                                    period = self.period,
                                    entry_type = self.type,
                                    reversal = False,
                                    lastchange_by = au,
                                    parent = self)
            gl_records.append(record)
            rounding_account.balance = rounding_account.balance - r
            accounts[rounding_account.code] = rounding_account
            
        db.put(gl_records)
        db.put(payments)
        db.put(accounts.values())
        return True, "General Ledger was updated"

class coowner_payment(db.Model):
    coowner = db.ReferenceProperty(reference_class = contact, verbose_name = "Coproprietaire", required=True)      
    immeuble =  db.ReferenceProperty(reference_class = immeuble, verbose_name = "Immeuble", required=True)
    type = db.StringProperty(verbose_name = "Type", required=True, default="Cheque", choices=set(["Cheque", "Cash", "Carte Blue"]))
    cheque_date = db.DateProperty(verbose_name = "Check recived date")
    bank_date = db.DateProperty(verbose_name = "Money recived date")
    fundraising = db.Reference(reference_class=fundraising, verbose_name = "Fundraising request", required=True)      
    payment_num = db.StringProperty(verbose_name = "Reference paiement", required=False)
    amount = db.FloatProperty(verbose_name = "Montant", required=True, default=0.0)
    commited = db.BooleanProperty(verbose_name = "Check commited", required=True, default = False)
    money_recived = db.BooleanProperty(verbose_name = "Payment confirmed by bank", required=True, default = False)
    lastchange_by = db.ReferenceProperty(APNETUser, required=True)
    lastchange_date = db.DateTimeProperty(auto_now=True)

    def __repr__(self):
        return unicode("%s. %s [%s]" % (self.ammount, self.coowner, self.date.strftime('%d/%m/%Y')))

    def update_general_ledger(self, au):
        if self.commited and self.money_recived:
            return False, "This request already commited."
        
        imm = self.immeuble 
        imm_id = imm.key().id()
        cp_id = self.key().id()
        
        bacct_key_name = "51200_t_" + str(imm_id) + "_" + str(self.fundraising.bankaccount.key().id())
        transient_account = subaccount.get_by_key_name(bacct_key_name, self.fundraising.bankaccount)
        if not transient_account:
            return False, "Transient bank account for '%s' should be defined" % unicode(self.fundraising.bankaccount)

        logging.info(self.type)
        
        gl_records = [] 
        accounts = []
        if self.type != "Carte Blue": 
            if not self.commited:
                owneracc_key_name = "45000_" + str(imm_id) + "_" + str(self.coowner.key().id())
                owner_account = subaccount.get_by_key_name(owneracc_key_name, self.coowner)
                if not owner_account:
                    return False, ("Account for '%s' not defined" ) % (unicode(self.coowner))
                if not self.payment_num:
                    return False, "Reference payment number should be defined"
                gl_records.append(general_ledger(source = 'Reception cheque coproprietaires',
                                                 number = 'CHKC_' + str(cp_id),
                                                 doc_date = self.cheque_date,
                                                 ini_subaccount = transient_account,
                                                 description = "%s: %s" % (self.fundraising.desc, self.coowner) ,
                                                 debit = self.amount,
                                                 credit = self.amount,
                                                 contr_subaccount = owner_account,
                                                 period = self.fundraising.period,
                                                 entry_type = self.type,
                                                 payment_num = self.payment_num,
                                                 reversal = False,
                                                 lastchange_by = au))
                self.commited = True
                transient_account.balance = round((transient_account.balance - self.amount),2)
                owner_account.balance = round((owner_account.balance + self.amount),2) 
                accounts.append(transient_account)
                accounts.append(owner_account)
                
            elif not self.money_recived:
                bacc_key_name =  "51200_" + str(imm_id) + "_" + str(self.fundraising.bankaccount.key().id())
                bank_account = subaccount.get_by_key_name(bacc_key_name, self.fundraising.bankaccount)
                if not bank_account:
                    return False, ("Account for '%s' not defined" ) % (unicode(self.bankaccount))
                if not  self.bank_date:
                    return False, ("Money received date not defined" )
                gl_records.append(general_ledger(source = 'Encaissement des coproprietaires',
                                                 number = 'EDC_' + str(cp_id),
                                                 doc_date = self.bank_date,
                                                 ini_subaccount = bank_account,
                                                 description = "Encaissement Cheque %s" % self.payment_num,
                                                 debit = self.amount,
                                                 credit = self.amount,
                                                 contr_subaccount = transient_account,
                                                 period = self.fundraising.period,
                                                 entry_type = self.type,
                                                 reversal = False,
                                                 lastchange_by = au))
                self.money_recived = True
                transient_account.balance = round((transient_account.balance + self.amount),2)
                bank_account.balance = round((bank_account.balance - self.amount),2)
                accounts.append(transient_account)
                accounts.append(bank_account)
        else:
            bacc_key_name =  "51200_" + str(imm_id) + "_" + str(self.fundraising.bankaccount.key().id())
            bank_account = subaccount.get_by_key_name(bacc_key_name, self.fundraising.bankaccount)
            if not bank_account:
                return False, ("Account for '%s' not defined" ) % (unicode(self.fundraising.bankaccount)) 

            owneracc_key_name = "45000_" + str(imm_id) + "_" + str(self.coowner.key().id())
            owner_account = subaccount.get_by_key_name(owneracc_key_name, self.coowner)
            if not owner_account:
                return False, ("Account for '%s' not defined" ) % (unicode(self.coowner))
            
            gl_records.append(general_ledger(source = 'Encaissement des coproprietaires',
                                             number = 'EDC_' + str(cp_id),
                                             doc_date = self.bank_date,
                                             ini_subaccount = bank_account,
                                             description = "%s: %s" % (self.fundraising.desc, self.coowner ),
                                             debit = self.amount,
                                             credit = self.amount,
                                             contr_subaccount = owner_account,
                                             period = self.fundraising.period,
                                             entry_type = self.type,
                                             reversal = False,
                                             lastchange_by = au))
            bank_account.balance = round((bank_account.balance - self.amount),2)
            owner_account.balance = round((owner_account.balance + self.amount),2)
            accounts.append(owner_account)
            accounts.append(bank_account)
            self.money_recived = True
            self.commited = True

        db.put(gl_records)
        db.put(accounts)
        self.put()
        
        return True, "General Ledger was updated"


    
class diverse_operations(db.Model):
    od_date = db.DateProperty(verbose_name = "Date", default = datetime.date.today(),required=True) 
    immeuble =  db.ReferenceProperty(reference_class = immeuble, verbose_name = "Immeuble", required=True)
    subaccount_debit = db.ReferenceProperty(reference_class = subaccount, verbose_name = "Debit account", collection_name = 'debit', required=True)
    subaccount_credit = db.ReferenceProperty(reference_class = subaccount, verbose_name = "Credit account", collection_name = 'credit', required=True)  
    journal = db.StringProperty(verbose_name = "Journal", required=True, choices=set(["Achat","Nouveau","Banque","Operations Diverses"]))
    number = db.StringProperty(verbose_name = "# de piece", required=True)
    lable = db.StringProperty(verbose_name = "Libelle", required=False)
    amount = db.FloatProperty(verbose_name = "Montant TTC", required=True)
    tva = db.FloatProperty(verbose_name = "dont TVA", default=0.0)
    rental_charges = db.FloatProperty(verbose_name = "Montant charges locatives", required=False, default=0.0) 
    tax_deduction = db.FloatProperty(verbose_name = "Montant deduction fiscale", required=False, default=0.0)
    property_income_deduction = db.FloatProperty(verbose_name = "Montant deduction revenu foncier", required=False, default=0.0)
    lastchange_by = db.ReferenceProperty(APNETUser, required=True)
    lastchange_date = db.DateTimeProperty(auto_now=True)
    
    def __repr__(self):
        return unicode("#%s: %s [%s]" % (self.number, self.title, self.amount))    
    
class journal(db.Model):
    period = db.ReferenceProperty(reference_class = period_concept, verbose_name = "Account period", required=True)
    type = db.StringProperty(verbose_name = "Journal", required=True, choices=set(["Achat","Nouveau","Banque","Operations Diverses"]))
    
class general_ledger(db.Expando):
    input_date = db.DateTimeProperty(verbose_name = "Input date", auto_now_add=True ,required=True)
    source = db.StringProperty(verbose_name = "Source", required=True)
    number = db.StringProperty(verbose_name = "N", required=True)
    doc_date = db.DateProperty(verbose_name = "Date", required=True)
    ini_subaccount = db.ReferenceProperty(reference_class = subaccount, verbose_name="Account number", required=True, collection_name ='gl_account_set')
    description = db.StringProperty(verbose_name = "Description")
    debit = db.FloatProperty(verbose_name = "Debit")
    credit = db.FloatProperty(verbose_name = "Credit")
    contr_subaccount = db.ReferenceProperty(reference_class = subaccount, verbose_name="Counter part", required=True, collection_name ='gl_contr_account_set')
    period = db.ReferenceProperty(reference_class = period_concept, verbose_name = "Account period", required=True)
    entry_type = db.StringProperty(verbose_name = "Type of entry")
    payment_num = db.StringProperty(verbose_name = "Payment reference")
    reversal = db.BooleanProperty(verbose_name = "Extourne", default=False)
    lastchange_by = db.ReferenceProperty(APNETUser, required=True)
    lastchange_date = db.DateTimeProperty(auto_now=True)
